﻿namespace DomainServices.Tools.SharedTypes
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;

    internal class SourceFileLocationService : ISourceFileLocationService, IDisposable
    {
        private Dictionary<MemberInfo, int> _fileIdsByMemberInfo = new Dictionary<MemberInfo, int>();
        private FilenameMap _filenameMap;
        private Dictionary<Type, IEnumerable<MemberInfo>> _memberInfosByType = new Dictionary<Type, IEnumerable<MemberInfo>>();
        private ISourceFileProvider[] _providers;

        internal SourceFileLocationService(IEnumerable<ISourceFileProviderFactory> providerFactories, FilenameMap filenameMap)
        {
            this._filenameMap = filenameMap;
            this._providers = (from f in providerFactories select f.CreateProvider()).ToArray<ISourceFileProvider>();
        }

        private void AddMemberInfoToCache(MemberInfo memberInfo, ISourceFileProvider provider)
        {
            if (!this._fileIdsByMemberInfo.ContainsKey(memberInfo))
            {
                string fileForMember = provider.GetFileForMember(memberInfo);
                if (!string.IsNullOrEmpty(fileForMember))
                {
                    int num = this._filenameMap.AddOrGet(fileForMember);
                    this._fileIdsByMemberInfo[memberInfo] = num;
                }
            }
        }

        public void Dispose()
        {
            foreach (ISourceFileProvider provider in this._providers)
            {
                provider.Dispose();
            }
            this._providers = new ISourceFileProvider[0];
        }

        public string GetFileForMember(MemberInfo memberInfo)
        {
            if (memberInfo == null)
            {
                throw new ArgumentNullException("memberInfo");
            }
            this.GetMembersForType(memberInfo.DeclaringType);
            string str = null;
            int num = -1;
            if (this._fileIdsByMemberInfo.TryGetValue(memberInfo, out num))
            {
                str = this._filenameMap[num];
                if (str.Length == 0)
                {
                    str = null;
                }
            }
            return str;
        }

        public IEnumerable<string> GetFilesForType(Type type)
        {
            if (type == null)
            {
                throw new ArgumentNullException("type");
            }
            return (from i in (from m in this.GetMembersForType(type) select this._fileIdsByMemberInfo[m]).Distinct<int>()
                select this._filenameMap[i] into f
                where !string.IsNullOrEmpty(f)
                select f);
        }

        private IEnumerable<MemberInfo> GetMembersForType(Type type)
        {
            IEnumerable<MemberInfo> enumerable = null;
            if (!this._memberInfosByType.TryGetValue(type, out enumerable))
            {
                enumerable = this.LoadAllMembersForType(type);
                this._memberInfosByType[type] = enumerable;
            }
            return enumerable;
        }

        private IEnumerable<MemberInfo> LoadAllMembersForType(Type type)
        {
            List<MemberInfo> list = new List<MemberInfo>();
            foreach (ISourceFileProvider provider in this._providers)
            {
                MethodInfo[] methods = type.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
                for (int i = 0; i < methods.Length; i++)
                {
                    MethodBase item = methods[i];
                    list.Add(item);
                    this.AddMemberInfoToCache(item, provider);
                }
                foreach (ConstructorInfo info in type.GetConstructors(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance))
                {
                    list.Add(info);
                    this.AddMemberInfoToCache(info, provider);
                }
                foreach (PropertyInfo info2 in type.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance))
                {
                    list.Add(info2);
                    this.AddMemberInfoToCache(info2, provider);
                }
            }
            foreach (MemberInfo info3 in list)
            {
                if (!this._fileIdsByMemberInfo.ContainsKey(info3))
                {
                    int num = this._filenameMap.AddOrGet(null);
                    this._fileIdsByMemberInfo[info3] = num;
                }
            }
            return list;
        }
    }
}

